package MtG.server.components;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.logging.Level;
import MtG.publicClass.Game;
import MtG.publicClass.Player;
import MtG.publicClass.messages.CardPlaced;
import MtG.publicClass.messages.Connected;
import MtG.publicClass.messages.Disconnected;
import MtG.publicClass.messages.EndTurn;
import MtG.publicClass.messages.Winner;
import MtG.server.ServerRun;

	/**
	 * GamePanel.java is the class that designs the games "board" area 
	 * where all the cards in play will be situated.
	 * @author Marek Sirp
	 * @author Jaan Sepp
	 */
	public class Communication extends Thread {
		/**
		 * Socket that this server listens to.
		 */
		private Socket sock;	
	    /**
	     * Input stream of this server.
	     */
	    private ObjectInputStream netIn;
		/**
		 * Output stream of this server.
		 */
		private ObjectOutputStream netOut;
		/**
		 * This servers object handling object.
		 */
		private GameMoves gameMove;
		/**
		 * The client handling object of this server.
		 */
		private ActiveSession activeSession;
		/**
		 * The game that is played on this server.
		 */
		private Game game;
		/**
		 * The current player
		 */
		private Player user;
		/**
		 * The error logging object for this server.
		 */
		private ErrorLogger logger;		
		/**
		 * boolean which determines if game have enough players for running;
		 */
		private boolean running = true;
		
		
		/**
		 * Constructor.
		 * @param sock current socket.
		 * @param gameMove current object.
		 * @param activeSession current list of clients.
		 * @param game current game.
		 * @param logger current log
		 * @throws IOException caused by input and output streams. 
		 */
		public Communication(Socket sock, GameMoves gameMove, ActiveSession activeSession, Game game, ErrorLogger logger) throws IOException {
			this.sock = sock;
			this.gameMove = gameMove;
			this.activeSession = activeSession;
			this.game = game;
			this.logger = logger;

			try {
			      netIn = new ObjectInputStream(this.sock.getInputStream());
			      netOut = new ObjectOutputStream(this.sock.getOutputStream());
			     } catch(Exception e1) {
			         try {
			           sock.close();
			         }catch(Exception e) {
			           logger.login("Failed to establish a connection." + e.getMessage(), Level.WARNING);
			         }
			     }			
			start();
		}
		
	/* (non-Javadoc)
	 * @see java.lang.Thread#run()
	 */
	public void run(){		
		try {
			Player player = null;
			player = (Player)netIn.readObject();
			this.user = player;
			gameMove.addGameMove(new Connected(user));
			setName(player.getName());		
			activeSession.addSession(this);
			try {
				while(!game.isStarted() && !sock.isClosed()){
					game.addPlayer(player);
					game.waitingForPlayers();
					sendGameMove(new Player());
				}
			} catch (InterruptedException e) {
				logger.login("Failed to connect to game.", Level.INFO);				
			}

			if(running){
				sendGameMove(game);
				sendGameMove(new EndTurn(game.getWhoseTurn()));
			}

			while(running){
				Object temp = netIn.readObject();
				if(temp == null){
					running = false;
				} else if(temp instanceof Player){
					player = (Player)temp;
					if(!player.getPlayersCardsInPlay().isEmpty() && !player.getPlayersCardsInPlay().
						get(player.getPlayersCardsInPlay().size()-1).isUseable() &&
						player.getPlayersCardsInPlay().get(player.getPlayersCardsInPlay().size()-1).getControlCode().equals(player.getLastClickedCard())){

						gameMove.addGameMove(new CardPlaced(player.getPlayersCardsInPlay().
							get(player.getPlayersCardsInPlay().size()-1), player));
					}
					gameMove.addGameMove(player);
				} else {
					game = (Game)temp;
					game.move();
					gameMove.addGameMove(game);
					gameMove.addGameMove(game.getMessage());
					if(game.isWinner()){
						System.out.println("voit");
						gameMove.addGameMove(new Winner(game.whoIsWinner()));
						gameMove.addGameMove(null);
					}
				}
			}		
		} catch (IOException e1) {
			System.out.println("--------Error 2----------");
			logger.login("Connection lost.", Level.WARNING);
			System.out.println("--------Error 2----------");
		} catch (ClassNotFoundException e2) {
			System.out.println("--------Error 3----------");
			logger.login("Sendable object not found", Level.WARNING);
			System.out.println("--------Error 3----------");
		} finally {
			try {
				sock.close();
				gameMove.addGameMove(new Disconnected(user));
				if(!user.isSpectator()){
					ServerRun.game = new Game();
					running = false;
					gameMove.addGameMove(null);
				}
			} catch (IOException e) {
				logger.login("Failed to close socket connection", Level.WARNING);
			}
		}
}
	

		
		/**
		 * @param object object that is to be sent using the output stream.
		 */
		public synchronized void sendGameMove(Object object) {
			try {
				if (!sock.isClosed()) {
					netOut.writeObject((Object)object);
					netOut.flush();
					netOut.reset();
				}
			} catch (IOException eee) {
				System.out.println("--------Error 1----------");
				logger.login("Connection lost on sending object.", Level.WARNING);
				System.out.println("--------Error 1----------");
				running = false;
				try {
					sock.close();
				} catch (IOException ee) {
					logger.login("Failed to close socket connection", Level.WARNING);
				}			
			}
		}
	}

